package cn.hg.solon.youcan.web.admin;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.stp.StpUtil;
import cn.hg.solon.youcan.common.constant.CacheConstants;
import cn.hg.solon.youcan.common.enums.BusinessType;
import cn.hg.solon.youcan.framework.cache.RedisCacheTagsService;
import cn.hg.solon.youcan.framework.satoken.SaUtil;
import cn.hg.solon.youcan.framework.service.LoginService;
import cn.hg.solon.youcan.framework.service.LogoutService;
import cn.hg.solon.youcan.framework.service.PearService;
import cn.hg.solon.youcan.framework.web.admin.annotation.Logging;
import cn.hg.solon.youcan.framework.web.pear.PearMenuItem;
import cn.hg.solon.youcan.system.entity.Config;
import cn.hg.solon.youcan.system.entity.User;
import cn.hg.solon.youcan.system.service.ConfigService;
import cn.hg.solon.youcan.system.service.UserService;
import cn.hg.solon.youcan.web.BaseAdminController;
import com.jfinal.template.Engine;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.lang.Validator;
import org.dromara.hutool.core.text.CharSequenceUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Controller;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.annotation.Param;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.MethodType;
import org.noear.solon.core.handle.ModelAndView;
import org.noear.solon.core.handle.Result;
import org.noear.solon.validation.annotation.Length;
import org.noear.solon.validation.annotation.NotEmpty;

import java.util.List;
import java.util.function.Supplier;

/**
 * @author 胡高
 */
@Controller
@Mapping("/admin")
public class AdminIndexController extends BaseAdminController {

    private static String SERVICE_NAME = "管理平台";

    private static String VIEW_BASE = "/admin/";

    @Inject
    private UserService userService;

    @Inject
    private LoginService loginService;

    @Inject
    private LogoutService logoutService;

    @Inject
    private RedisCacheTagsService cacheService;

    @Inject
    private PearService pearService;

    @Inject
    private ConfigService configService;

    /**
     * 打开修改密码页面
     */
    @Mapping(path = "changePassword", method = MethodType.GET)
    @SaCheckLogin
    public ModelAndView changePassword(Context ctx) {
        this.log.info("{}打开修改密码页面：", SERVICE_NAME);

        ModelAndView mav = new ModelAndView(VIEW_BASE + "changePassword.html");

        return mav;
    }

    /**
     * 修改密码操作
     */
    @Mapping(path = "changePassword", method = MethodType.PUT)
    @Logging(title = "重置密码", businessType = BusinessType.OTHER, isSaveRequestData = false)
    @SaCheckLogin
    public Result<?> changePassword(Context ctx,
                                    @NotEmpty @Length(min = 6, max = 20, message = "旧密码长度必须 6 到 20 字符") String oldPassword,
                                    @NotEmpty @Length(min = 6, max = 20, message = "新密码长度必须 6 到 20 字符") String newPassword,
                                    @NotEmpty(message = "必须输入确认密码") String confirmPassword) {
        this.log.info("{}执行修改密码操作：", SERVICE_NAME);

        Validator.validateEqual(newPassword, confirmPassword, "新密码与确认密码不一致");

        this.userService.resetPassword(SaUtil.getCurrentUser(), oldPassword, newPassword, SaUtil.getCurrentUser());

        return Result.succeed();
    }

    @Mapping(path = "config", method = MethodType.GET)
    @SaCheckLogin
    public Object config(Context ctx) {
        User user = SaUtil.getCurrentUser();
        Object config = this.cacheService.getOrStoreTag("index-config:" + user.getId(),
                Object.class, CacheConstants.CACHE_SECONDS_ONE_HOUR, new Supplier<Object>() {
                    @Override
                    public Object get() {
                        Config c = AdminIndexController.this.configService.getByTypeAndCode("APP", "admin_config");

                        /*
                         * 如果系统配置不存在或配置中的值为空，则采用系统配置
                         */
                        if (ObjUtil.isNull(c) || StrValidator.isBlank(c.getValue())) {
                            return AdminIndexController.this.pearService.getConfig();
                        }

                        return Engine.use().getTemplateByString(c.getValue()).renderToString();
                    }
                }, CacheConstants.SYS_USER_TAG);

        return config;
    }

    /**
     * Home页面
     */
    @Mapping(path = "home", method = MethodType.GET)
    @SaCheckLogin
    public ModelAndView home(Context ctx) {
        this.log.info("{}打开Home页面：", SERVICE_NAME);

        ModelAndView mav = new ModelAndView(VIEW_BASE + "home.html");

        return mav;
    }

    /**
     * 首页
     */
    @Mapping(path = "", method = MethodType.GET)
    @SaCheckLogin
    public ModelAndView index(Context ctx) {
        this.log.info("打开管理端页面：index.html");

        ModelAndView mav = new ModelAndView(VIEW_BASE + "index.html");

        mav.put("user", this.loginService.currentUser());

        return mav;
    }

    /**
     * 跳转入登录页面
     */
    @Mapping(path = "login", method = MethodType.GET)
    public ModelAndView login(Context ctx) {
        this.log.info("{}打开登录页面：", SERVICE_NAME);

        ModelAndView mav = new ModelAndView(VIEW_BASE + "login.html");

        if (StpUtil.isLogin()) {
            ctx.redirect((StrValidator.isBlank(Solon.cfg().serverContextPath()) ? "/" : Solon.cfg().serverContextPath())
                    + "admin");
            return null;
        }

        // 将请求参数原样传递到页面
        mav.putAll(ctx.paramMap().toValueMap());

        return mav;
    }

    /**
     * 登录操作
     */
    @Mapping(path = "login", method = MethodType.POST)
    public Result<?> login(Context ctx, @NotEmpty String captchaVerification,
                           @NotEmpty @Length(min = 3, max = 20, message = "账号长度必须 3 到 20 字符") String account,
                           @NotEmpty @Length(min = 6, max = 20, message = "密码长度必须 6 到 20 字符") String password, String remember) {
        this.log.info("{}登录操作：", SERVICE_NAME);

        this.loginService.login(account, password, captchaVerification, CharSequenceUtil.equals("on", remember));

        return Result.succeed();
    }

    /**
     * 登出操作
     */
    @Mapping(path = "logout", method = {MethodType.GET, MethodType.DELETE})
    public Result<?> logout(Context ctx) {
        this.logoutService.logout();
        return Result.succeed();
    }

    /**
     * 获取菜单数据
     */
    @Mapping(path = "menu", method = MethodType.GET)
    @SaCheckLogin
    public List<PearMenuItem> menu(Context ctx, @Param String ids) {
        this.log.debug("{}打开管理端菜单：ids={}", SERVICE_NAME, ids);
        User user = SaUtil.getCurrentUser();

        @SuppressWarnings({"unchecked", "rawtypes"})
        List<PearMenuItem> menus =
                this.cacheService.getOrStoreTag("index-menu:" + user.getId() + ":" + ids, List.class,
                        CacheConstants.CACHE_SECONDS_ONE_HOUR, new Supplier<List>() {
                            @Override
                            public List<PearMenuItem> get() {
                                return AdminIndexController.this.pearService.buildMenu(user,
                                        ConvertUtil.toIntArray(SplitUtil.split(ids, ",")));
                            }
                        }, CacheConstants.SYS_USER_TAG);

        return menus;
    }
}
